package jp.co.nes.awssupport.test.utility;

import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Assertユーティリティです。
 */
public class AssertUtil {

    /** ロガー */
    private static Logger logger = LoggerFactory.getLogger(AssertUtil.class);

    /**
     * デフォルトコンストラクタです。<br>
     * 本クラスはインスタンスを生成しません。
     */
    private AssertUtil() {
    }

    /**
     * アサーションです。
     *
     * @param suffixReason reasonのsuffix
     * @param actualBean 結果値
     * @param expectedBean 期待値
     * @param exclusionMethods 除外するメソッド(デフォルトで除外しているもの以外に存在する場合に指定)
     */
    public static <T> void assertBean(String suffixReason, T actualBean, T expectedBean, String... exclusionMethods) {
        assertThat(expectedBean, notNullValue());
        assertThat(actualBean, notNullValue());

        Class<?> actualClass = actualBean.getClass();
        Class<?> expectedClass = expectedBean.getClass();
        Method[] expectedMethods = expectedClass.getMethods();
        methodLoop: for (Method expectedMethod : expectedMethods) {
            String methodName = expectedMethod.getName();
            if (!methodName.startsWith("get") || methodName.startsWith("getClass")) {
                continue;
            }
            for (String exclusionMethod : exclusionMethods) {
                if (methodName.startsWith(exclusionMethod)) {
                    continue methodLoop;
                }
            }

            try {
                Method actualMethod = actualClass.getMethod(methodName, new Class<?>[] {});
                Object actualObj = actualMethod.invoke(actualBean);
                Object expectedObj = expectedMethod.invoke(expectedBean);
                if (logger.isDebugEnabled()) {
                    logger.debug("# method=" + methodName + ", actual=" + actualObj + ", expected=" + expectedObj);
                }
                assertThat("methodName=" + methodName + suffixReason, actualObj, is(expectedObj));
            } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException
                    | InvocationTargetException e) {
                throw new RuntimeException(e);
            }
        }
    }

    /**
     * アサーションです。
     *
     * @param actualBean 結果値
     * @param expectedBean 期待値
     */
    public static <T> void assertBean(T actualBean, T expectedBean) {
        assertBean("", actualBean, expectedBean);
    }

}
